home *** CD-ROM | disk | FTP | other *** search
- ; PPWAV.ASM
- ;
- ; PreProcess Wav: this program performs one or more of the following
- ; conversions on a .wav file: mix to mono, convert to 8-bit, and halve
- ; sampling rate. This program is provided for users of PLAYWAV who have
- ; been confonted with the "Output underflow" message. Its purpose is to
- ; reduce the size of a .wav file so that it can be played on limited
- ; hardware (such as a floppy-only system, or to a lesser extent the SL).
- ; Note that mixing to mono and converting to 8-bit with PPWAV do not
- ; affect the sound quality when the .wav is to be played on a Tandy with
- ; PLAYWAV, since those conversions must be done when the .wav is played
- ; anyway.
- ; Some unusual .wav types that PLAYWAV can't play can also be con-
- ; verted with this program (more than 16 bits per channel, more than 2
- ; channels, sampling rate higher than 65535 Hz).
- ; The input file is copied rather then overwritten.
- ; This program is in .exe format due to memory requirements.
- ; Syntax is as follows:
- ;
- ; PPWAV <input file> <output file>
- ;
- ; There are no options. The user will be prompted for input from the key-
- ; board.
- ;
- ; Order of segments:
- ;
-
- SCODE SEGMENT
- SCODE ENDS
- SDATA SEGMENT
- SDATA ENDS
- ;
- ; Stack segment.
- ;
- STACK SEGMENT STACK
- DW 512 DUP (?)
- STACK ENDS
- ;
- ; Buffer for output samples (16K).
- ;
- OUTBUF SEGMENT
- DB 16384 DUP (?)
- OUTBUF ENDS
- ;
- ; Buffer for input samples (32K).
- ;
- INBUF SEGMENT
- DB 32768 DUP (?)
- INBUF ENDS
-
- SDATA SEGMENT
- ;
- ; Data.
- ;
- ; Template for .wav header. This header will be filled in to match the
- ; output file and written out. The strings below are also used to verify
- ; the input .wav.
- ;
- WAVHEADER EQU $
- RIFFSTR DB "RIFF" ; RIFF signature = "RIFF"
- WAVLEN DD 0 ; (length of .wav file) - 8
- WAVESTR DB "WAVE" ; WAVE signature = "WAVE"
- FMTSTR DB "fmt " ; format chunk header = "fmt "
- DD 16 ; length of format chunk = 16
- DW 1 ; format type = 1 (Microsoft PCM)
- NCHANNELS DW 0 ; number of channels
- SAMPRATE DD 0 ; samples per second
- BYTESPERSEC DD 0 ; bytes per second
- SAMPSIZE DW 0 ; bytes per (multichannel) sample
- SAMPWIDTH DW 0 ; bits per channel
- DATASTR DB "data" ; data block header = "data"
- DATALEN DD 0 ; number of bytes in data chunk
- ;
- ; Additional output parameters.
- ;
- CHANSIZE DW 0 ; bytes per channel
- ISSIGNED DB 0 ; 1 = output samples are signed
- NSAMPLES DD 0 ; number of samples in output file
- ;
- ; Flag, 1 = format chunk processed.
- ;
- FMTDONE DB 0
- ;
- ; Input file format parameters.
- ;
- INISSIGNED DB 0 ; 1 = input samples are signed
- INNCHANNELS DW 0 ; number of channels
- INSAMPRATE DD 0 ; sampling rate
- INSAMPWIDTH DW 0 ; bits per channel
- INCHANSIZE DW 0 ; bytes per channel
- INSAMPSIZE DW 0 ; bytes per (multichannel) sample
- INNSAMPLES DD 0 ; number of samples in input file
- ;
- ; Small buffer for reading header fields and for processing
- ; samples.
- ;
- SMALLBUF DB 16 DUP (0)
- ;
- ; Small buffers for mixing channels.
- ;
- MIXBUF DB 9 DUP (0)
- CHANBUF DB 9 DUP (0)
- ;
- ; Number of input samples needed to make 1024 output samples.
- ;
- SAMPTOREAD DW 1024 ; change to 2048 if HALVEFLAG on
- ;
- ; Number of bytes to read to get 1024 output samples.
- ;
- BYTESTOREAD DW 0
- ;
- ; Number of bytes from the input buffer that have been
- ; processed.
- ;
- BYTESTAKEN DW 0
- ;
- ; Number of bytes placed in the output buffer.
- ;
- BYTESPLACED DW 0
- ;
- ; Number of input bytes on each channel to skip over.
- ;
- SKIPLENGTH DW 0
- ;
- ; Error messages.
- ;
- USAGEMSG DB "Usage: PPWAV <input file> <output file>",0Dh,0Ah
- DB "- see docs for details.",0Dh,0Ah,"$"
- INOPENMSG DB "Error opening input file.",0Dh,0Ah,"$"
- OUTOPENMSG DB "Unable to create output file.",0Dh,0Ah,"$"
- READERRMSG DB "Error reading input .wav file.",0Dh,0Ah,"$"
- WRITEERRMSG DB "Error writing output .wav file.",0Dh,0Ah,"$"
- BADWAVMSG DB "Input .wav file invalid or unsupported type."
- DB 0Dh,0Ah,"$"
- ;
- ; File handles.
- ;
- INHANDLE DW 0 ; input file handle
- OUTHANDLE DW 0 ; output file handle
- ;
- ; Information about the input file to be displayed to the user.
- ;
- MSGA DB "Input file: $"
- INFILENAME DB 128 DUP (0) ; input file name
- MSGB DB 0Dh,0Ah,9,"$"
- MSGC DB " channels",0Dh,0Ah,9,"$"
- MSGD DB " samples per second (Hz)",0Dh,0Ah,9,"$"
- MSGE DB " bits per channel",0Dh,0Ah,9,"$"
- MSGF DB "samples are signed",0Dh,0Ah,0Dh,0Ah,"$"
- MSGG DB "samples are unsigned",0Dh,0Ah,0Dh,0Ah,"$"
- ;
- ; Action flags. 1 = action selected.
- ;
- MIXFLAG DB 0
- CONVERTFLAG DB 0
- HALVEFLAG DB 0
- ;
- ; Prompts for the user.
- ;
- MONOMSG DB " Mix to mono?$"
- CONVERTMSG DB " Convert to 8-bit?$"
- HALVEMSG DB "Halve sampling rate?$"
- YHIMSG DB " (Y/n) $"
- NHIMSG DB " (y/N) $"
- ;
- ; Buffer for user input.
- ;
- USERBUF DB 2
- ANSWERED DB 0 ; = 1 if the user answered
- ANSWER DB 2 DUP (0) ; user's answer in first byte
- DATA ENDS
-
- SCODE SEGMENT
- ;
- ; Error handling subroutine. Displays the string addressed by DS:DX and
- ; halts the program.
- ;
- ERROR:
- MOV AH,9
- INT 21h
- MOV AX,4C01h ; return ERRORLEVEL 1
- INT 21h
- ;
- ; Subroutine, takes pointer to string in DS:SI and length of string in CX,
- ; skips over blanks and tabs, returns pointer to first nonblank character
- ; in the string in DS:SI, length of remaining string in CX. If end of
- ; string is reached, return pointer to end of string in DS:SI, zero in CX.
- ;
- SKIPBLANKS:
- PUSH AX
- SKIPBLANKS_LOOP:
- JCXZ SKIPBLANKS_END
- LODSB
- DEC CX
- CMP AL,9
- JE SKIPBLANKS_LOOP
- CMP AL,20h
- JE SKIPBLANKS_LOOP
- DEC SI
- INC CX
- SKIPBLANKS_END:
- POP AX
- RET
- ;
- ; Subroutine, takes pointer to string is DS:SI and length of string in CX,
- ; skips over nonblank characters, returns pointer to first blank or tab in
- ; the string in DS:SI, length of remaining string in CX. If end of string
- ; is reached, return pointer to end of string in DS:SI, zero in CX.
- ;
- SKIPNONBLANK:
- PUSH AX
- SKIPNONBLANK_LOOP:
- JCXZ SKIPNONBLANK_END
- LODSB
- DEC CX
- CMP AL,9
- JE SKIPNONBLANK_LPEND
- CMP AL,20h
- JNE SKIPNONBLANK_LOOP
- SKIPNONBLANK_LPEND:
- DEC SI
- INC CX
- SKIPNONBLANK_END:
- POP AX
- RET
- ;
- ; Subroutine, reads input and output filenames from the command line, opens
- ; the first file, and creates the second file. Displays a usage message if
- ; two filenames are not specified.
- ;
- OPENFILES:
- PUSH AX
- PUSH BX
- PUSH CX
- PUSH DX
- PUSH SI
- PUSH DI
- ;
- ; Exchange DS, ES. DS addresses PSP; ES addresses local data.
- ;
- PUSH DS
- PUSH ES
- POP DS
- POP ES
- ;
- ; Find first command line parameter, save pointer to it in DI.
- ;
- MOV CL,[80h]
- XOR CH,CH
- MOV SI,81h
- CALL SKIPBLANKS
- JCXZ OPENFILES_USAGEERR
- MOV DI,SI
- ;
- ; Find the end of it and save its length in BX.
- ;
- MOV DX,SI ; for Int 21h fcn 3Dh
- CALL SKIPNONBLANK
- JCXZ OPENFILES_USAGEERR
- MOV BX,SI
- SUB BX,DI
- ;
- ; Append a null.
- ;
- MOV BYTE PTR [SI],0
- INC SI
- DEC CX
- JCXZ OPENFILES_USAGEERR
- ;
- ; Open the input file.
- ;
- MOV AX,3D20h ; open read-only, deny writes
- INT 21h
- JC OPENFILES_OPEN1ERR
- MOV ES:INHANDLE,AX
- ;
- ; Find the second command line parameter.
- ;
- CALL SKIPBLANKS
- JCXZ OPENFILES_USAGEERR
- ;
- ; Append a null.
- ;
- MOV DX,SI ; for Int 21h fcn 3Ch
- CALL SKIPNONBLANK
- MOV BYTE PTR [SI],0
- ;
- ; Create the output file.
- ;
- MOV AH,3Ch
- XOR CX,CX ; attribute: normal file
- INT 21h
- JC OPENFILES_OPEN2ERR
- MOV ES:OUTHANDLE,AX
- ;
- ; Copy the input filename into the data segment.
- ;
- MOV SI,DI ; saved above
- MOV CX,BX ; also
- MOV DI,OFFSET INFILENAME
- REP MOVSB
- MOV BYTE PTR ES:[DI],"$"
- ;
- ; Swap DS, ES back.
- ;
- PUSH DS
- PUSH ES
- POP DS
- POP ES
- ;
- ; Restore registers and exit.
- ;
- POP DI
- POP SI
- POP DX
- POP CX
- POP BX
- POP AX
- RET
- ;
- ; Two files were not specified on the command line.
- ;
- OPENFILES_USAGEERR:
- PUSH ES
- POP DS
- MOV DX,OFFSET USAGEMSG
- JMP ERROR
- ;
- ; Unable to open input file.
- ;
- OPENFILES_OPEN1ERR:
- PUSH ES
- POP DS
- MOV DX,OFFSET INOPENMSG
- JMP ERROR
- ;
- ; Unable to create output file.
- ;
- OPENFILES_OPEN2ERR:
- PUSH ES
- POP DS
- MOV DX,OFFSET OUTOPENMSG
- JMP ERROR
- ;
- ; Subroutine to read a small number of bytes (specified in CX) from the input
- ; file into SMALLBUF. Returns number of bytes in AX, pointer to SMALLBUF in
- ; DX.
- ;
- READSMALL:
- PUSH BX
- MOV AH,3Fh
- MOV BX,INHANDLE
- MOV DX,OFFSET SMALLBUF
- INT 21h
- JC READSMALL_READERR
- CMP AX,CX
- JNE READSMALL_INVALID
- POP BX
- RET
- ;
- ; Error reading .wav file header.
- ;
- READSMALL_READERR:
- MOV DX,OFFSET READERRMSG
- JMP ERROR
- ;
- ; Invalid .wav header, or unsupported .wav type.
- ;
- READSMALL_INVALID:
- MOV DX,OFFSET BADWAVMSG
- JMP ERROR
- ;
- ; Subroutine for GETWAVHEADER. Reads 4 bytes from the input file and checks
- ; whether the string read is "fmt ", "data", another ASCII string, or not
- ; ASCII. Returns AL = 0 if "fmt ", AL = 1 if "data", AL = -1 if another
- ; ASCII string. Halts the program if not ASCII. Assumes ES addresses data
- ; segment. Returns pointer to SMALLBUF in DX.
- ;
- GETCHUNK:
- PUSH BX
- PUSH CX
- PUSH SI
- PUSH DI
- ;
- ; Read 4 bytes from the file.
- ;
- MOV CX,4
- CALL READSMALL
- ;
- ; Check if format chunk.
- ;
- MOV SI,DX
- MOV DI,OFFSET FMTSTR
- REPE CMPSB
- JNE GETCHUNK_CHKDATA
- MOV AL,0
- JMP GETCHUNK_EXIT
- ;
- ; Check if data chunk.
- ;
- GETCHUNK_CHKDATA:
- MOV SI,DX
- MOV DI,OFFSET DATASTR
- MOV CX,4
- REPE CMPSB
- JNE GETCHUNK_CHKASCII
- MOV AL,1
- JMP GETCHUNK_EXIT
- ;
- ; Check if a valid chunk header of another type.
- ;
- GETCHUNK_CHKASCII:
- MOV SI,DX
- MOV CX,4
- GETCHUNK_ASCLOOP:
- LODSB
- CMP AL,20h
- JB GETCHUNK_INVALID
- CMP AL,7Eh
- JA GETCHUNK_INVALID
- LOOP GETCHUNK_ASCLOOP
- MOV AL,-1
- ;
- ; Read the chunk length field and exit.
- ;
- GETCHUNK_EXIT: PUSH AX
- MOV CX,4
- CALL READSMALL
- POP AX
- POP DI
- POP SI
- POP CX
- POP BX
- RET
- ;
- ; Invalid .wav header, or unsupported .wav type.
- ;
- GETCHUNK_INVALID:
- MOV DX,OFFSET BADWAVMSG
- JMP ERROR
- ;
- ; Subroutine for GETWAVHEADER. This routine reads the format chunk from the
- ; input file and records the information found there. Assumes DS:DX addresses
- ; the chunk length.
- ;
- DOFORMAT:
- PUSH AX
- PUSH BX
- PUSH CX
- PUSH DX
- PUSH SI
- ;
- ; Check chunk length (must be 16).
- ;
- MOV SI,DX
- LODSW
- CMP AX,16
- JNE DOFORMAT_INVALID
- MOV CX,AX
- LODSW
- OR AX,AX
- JNZ DOFORMAT_INVALID
- ;
- ; Read in format chunk.
- ;
- CALL READSMALL
- ;
- ; Verify format tag.
- ;
- MOV SI,DX
- LODSW
- CMP AX,1
- JNE DOFORMAT_INVALID
- ;
- ; Get number of channels and save.
- ;
- LODSW
- OR AX,AX ; invalid if zero channels
- JZ DOFORMAT_INVALID
- CMP AX,16 ; an impossibly large value ...
- JA DOFORMAT_INVALID
- MOV INNCHANNELS,AX
- ;
- ; Get sampling rate.
- ;
- LODSW
- MOV WORD PTR INSAMPRATE,AX
- MOV DX,AX
- LODSW
- OR DX,AX ; rate of zero invalid
- OR DX,DX
- JZ DOFORMAT_INVALID
- MOV WORD PTR INSAMPRATE+2,AX
- ;
- ; Get bits per channel.
- ;
- ADD SI,6 ; skip bytes/sec, block align
- LODSW
- OR AX,AX ; zero bits per sample invalid
- JZ DOFORMAT_INVALID
- CMP AX,128 ; an impossibly large value ...
- JA DOFORMAT_INVALID
- MOV INSAMPWIDTH,AX
- ;
- ; Compute bytes per channel.
- ;
- ADD AX,7
- SHR AX,1
- SHR AX,1
- SHR AX,1
- MOV INCHANSIZE,AX
- ;
- ; Compute bytes per (multichannel) sample.
- ;
- MUL INNCHANNELS
- CMP AX,16 ; this is what the buffer will hold ...
- JA DOFORMAT_INVALID
- MOV INSAMPSIZE,AX
- ;
- ; Are samples signed?
- ;
- CMP INCHANSIZE,2
- CMC
- RCL INISSIGNED,1
- ;
- ; Exit.
- ;
- POP SI
- POP DX
- POP CX
- POP BX
- POP AX
- RET
- ;
- ; Invalid .wav header, or unsupported .wav type.
- ;
- DOFORMAT_INVALID:
- MOV DX,OFFSET BADWAVMSG
- CALL ERROR
- ;
- ; Subroutine for GETWAVHEADER. This routine skips over an unknown chunk,
- ; updating the file pointer. Assumes DS:DX addresses the chunk length.
- ;
- SKIPCHUNK:
- PUSH AX
- PUSH BX
- PUSH CX
- PUSH DX
- PUSH SI
- MOV SI,DX
- LODSW
- MOV DX,AX
- LODSW
- MOV CX,AX
- MOV AX,4201h
- MOV BX,INHANDLE
- INT 21h
- POP SI
- POP DX
- POP CX
- POP BX
- POP AX
- RET
- ;
- ; Routine to read the .wav header from the input file and compute needed
- ; parameters from it. Skips unknown chunks.
- ;
- GETWAVHEADER:
- PUSH AX
- PUSH CX
- PUSH DX
- PUSH SI
- PUSH DI
- PUSH ES
- ;
- ; ES addresses data segment.
- ;
- MOV AX,DS
- MOV ES,AX
- ;
- ; Read RIFF and WAVE headers from file.
- ;
- MOV CX,12
- CALL READSMALL
- ;
- ; Verify "RIFF".
- ;
- MOV SI,DX
- MOV DI,OFFSET RIFFSTR
- MOV CX,4
- REPE CMPSB
- JNE GETWAVHEADER_INVALID
- ;
- ; Verify "WAVE".
- ;
- ADD SI,4 ; skip length field
- MOV DI,OFFSET WAVESTR
- MOV CX,4
- REPE CMPSB
- JNE GETWAVHEADER_INVALID
- ;
- ; Loop over chunks until data chunk found.
- ;
- GETWAVHEADER_LOOP:
- CALL GETCHUNK
- CMP AL,0 ; format chunk?
- JNE >L0
- CMP FMTDONE,1 ; error if > 1 format chunk
- JE GETWAVHEADER_INVALID
- CALL DOFORMAT
- MOV FMTDONE,1 ; mark format done
- JMP GETWAVHEADER_LOOP
- L0: CMP AL,1 ; data chunk?
- JNE >L1
- CMP FMTDONE,0 ; error if format chunk does
- JE GETWAVHEADER_INVALID ; not precede data chunk
- JMP GETWAVHEADER_LOOPEND
- L1: CALL SKIPCHUNK ; unknown chunk, skip
- JMP GETWAVHEADER_LOOP
- ;
- ; Data chunk found, exit.
- ;
- GETWAVHEADER_LOOPEND:
- POP ES
- POP DI
- POP SI
- POP DX
- POP CX
- POP AX
- RET
- ;
- ; Invalid .wav header, or unsupported .wav type.
- ;
- GETWAVHEADER_INVALID:
- MOV DX,OFFSET BADWAVMSG
- CALL ERROR
- ;
- ; Subroutine to display a decimal number on the screen. The number is in
- ; DX:AX.
- ;
- SHOWDECIMAL:
- PUSH AX
- PUSH BX
- PUSH CX
- PUSH DX
- PUSH SI
- PUSH DI
- MOV BX,10 ; BX = 10 (constant for division)
- XOR CX,CX ; CX counts the digits
- MOV DI,DX ; DI:SI holds the number while it's
- MOV SI,AX ; being divided away
- MOV AX,DX
- ;
- ; Divide away the number, saving remainders on the stack.
- ;
- SHOWDECIMAL_LOOP1:
- XOR DX,DX
- DIV BX
- MOV DI,AX
- MOV AX,SI
- DIV BX
- MOV SI,AX
- PUSH DX ; push digit (in low byte)
- INC CX ; bump count
- MOV AX,DI ; go again if not zero
- OR AX,AX
- JNZ SHOWDECIMAL_LOOP1
- OR SI,SI
- JNZ SHOWDECIMAL_LOOP1
- ;
- ; Pop the digits off the stack and display them. (CX is now the loop
- ; counter.)
- ;
- SHOWDECIMAL_LOOP2:
- POP DX
- ADD DL,'0'
- MOV AH,2
- INT 21h
- LOOP SHOWDECIMAL_LOOP2
- POP DI
- POP SI
- POP DX
- POP CX
- POP BX
- POP AX
- RET
- ;
- ; Routine to display information about the input .wav for the user.
- ;
- SHOWWAVINFO:
- PUSH AX
- PUSH DX
- ;
- ; Display input filename.
- ;
- MOV DX,OFFSET MSGA
- MOV AH,9
- INT 21h
- MOV DX,OFFSET INFILENAME
- MOV AH,9
- INT 21h
- MOV DX,OFFSET MSGB
- MOV AH,9
- INT 21h
- ;
- ; Display number of channels.
- ;
- MOV AX,INNCHANNELS
- XOR DX,DX
- CALL SHOWDECIMAL
- MOV DX,OFFSET MSGC
- MOV AH,9
- INT 21h
- ;
- ; Display sampling rate.
- ;
- MOV AX,WORD PTR INSAMPRATE
- MOV DX,WORD PTR INSAMPRATE+2
- CALL SHOWDECIMAL
- MOV DX,OFFSET MSGD
- MOV AH,9
- INT 21h
- ;
- ; Display bits per channel.
- ;
- MOV AX,INSAMPWIDTH
- XOR DX,DX
- CALL SHOWDECIMAL
- MOV DX,OFFSET MSGE
- MOV AH,9
- INT 21h
- ;
- ; Display whether samples are signed or unsigned.
- ;
- CMP INISSIGNED,1
- JNE >L0
- MOV DX,OFFSET MSGF
- JMP >L1
- L0:
- MOV DX,OFFSET MSGG
- L1:
- MOV AH,9
- INT 21h
- POP DX
- POP AX
- RET
- ;
- ; Subroutine to display a prompt to the user and get a "yes" or "no" answer.
- ; Takes 3 parameters. DS:DX addresses the prompt to be displayed (terminated
- ; with a dollar sign). DS:BX addresses the location where the answer will
- ; be stored (0 = no, 1 = yes). AL is the default answer (if the user just
- ; hits return or enters something other than "y" or "n").
- ;
- GETYN:
- PUSH AX
- PUSH CX
- PUSH DX
- ;
- ; Save default answer in CL.
- ;
- MOV CL,AL
- ;
- ; Display the prompt.
- ;
- MOV AH,9
- INT 21h
- ;
- ; Display "(Y/n)" if "yes" is the default, or "(y/N)" if "no" is
- ; the default.
- ;
- CMP CL,1
- JNE >L0
- MOV DX,OFFSET YHIMSG
- JMP >L1
- L0:
- MOV DX,OFFSET NHIMSG
- L1:
- MOV AH,9
- INT 21h
- ;
- ; Get the answer.
- ;
- MOV AH,0Ah
- MOV DX,OFFSET USERBUF
- INT 21h
- ;
- ; Set result according to answer.
- ;
- MOV [BX],CL ; assume no (default) answer
- CMP BYTE PTR ANSWERED,1 ; just exit if no answer
- JNE GETYN_EXIT
- ;
- ; Get answer in AL and convert to 0 or 1.
- ;
- MOV AL,ANSWER
- AND AL,0DFh ; convert to uppercase
- CMP AL,'Y'
- JNE >L0
- MOV AL,1
- JMP >L1
- L0:
- CMP AL,'N'
- JNE GETYN_EXIT
- MOV AL,0
- L1:
- MOV [BX],AL
- ;
- ; Move cursor to next line.
- ;
- GETYN_EXIT:
- MOV AH,2
- MOV DL,0Dh
- INT 21h
- MOV AH,2
- MOV DL,0Ah
- INT 21h
- POP DX
- POP CX
- POP AX
- RET
- ;
- ; Routine to prompt the user for the desired actions. The user may choose
- ; to do one or more of the following: mix to mono, convert to 8-bit, and/or
- ; cut the sampling rate in half.
- ;
- GETACTIONS:
- PUSH AX
- PUSH BX
- PUSH DX
- ;
- ; Ask if the user wants to mix to mono.
- ;
- MOV DX,OFFSET MONOMSG
- MOV BX,OFFSET MIXFLAG
- MOV AL,1
- CALL GETYN
- ;
- ; Ask if the user wants to convert to 8-bit.
- ;
- MOV DX,OFFSET CONVERTMSG
- MOV BX,OFFSET CONVERTFLAG
- MOV AL,1
- CALL GETYN
- ;
- ; Ask if the user wants to cut the sampling rate in half.
- ;
- MOV DX,OFFSET HALVEMSG
- MOV BX,OFFSET HALVEFLAG
- MOV AL,0
- CALL GETYN
- POP DX
- POP BX
- POP AX
- RET
- ;
- ; Routine to fill in the fields in the output .wav header and write the
- ; header to the output file.
- ;
- MAKEHEADER:
- PUSH AX
- PUSH BX
- PUSH CX
- PUSH DX
- PUSH SI
- PUSH DI
- ;
- ; Determine number of channels in the output file.
- ;
- CMP MIXFLAG,0
- JNE >L0
- MOV AX,INNCHANNELS ; no mixing
- MOV NCHANNELS,AX
- JMP >L1
- L0:
- MOV NCHANNELS,1 ; mix to mono
- ;
- ; Determine sampling rate for output file.
- ;
- L1:
- MOV AX,WORD PTR INSAMPRATE
- MOV DX,WORD PTR INSAMPRATE+2
- CMP HALVEFLAG,0
- JE >L2
- SHR DX,1 ; cut sampling rate in half
- RCR AX,1
- L2:
- MOV WORD PTR SAMPRATE,AX
- MOV WORD PTR SAMPRATE+2,DX
- ;
- ; Determine number of bits per channel for the output file.
- ;
- CMP CONVERTFLAG,0
- JNE >L3
- MOV AX,INSAMPWIDTH ; do not convert to 8-bit
- MOV SAMPWIDTH,AX
- MOV AX,INCHANSIZE
- MOV CHANSIZE,AX
- JMP >L4
- L3:
- MOV SAMPWIDTH,8 ; convert to 8-bit
- MOV AX,1
- MOV CHANSIZE,AX
- ;
- ; Determine whether output samples are signed.
- ;
- L4:
- CMP AX,2
- CMC
- RCL ISSIGNED,1
- ;
- ; Determine number of bytes per (multichannel) sample.
- ;
- MUL NCHANNELS
- MOV SAMPSIZE,AX
- ;
- ; Determine number of bytes per second.
- ;
- MOV AX,WORD PTR SAMPRATE
- MUL SAMPSIZE
- MOV WORD PTR BYTESPERSEC,AX
- MOV BX,DX
- MOV AX,WORD PTR SAMPRATE+2
- MUL SAMPSIZE
- ADD AX,BX
- MOV WORD PTR BYTESPERSEC+2,AX
- ;
- ; Determine number of (multichannel) samples from length of input
- ; file.
- ;
- MOV AX,4201h ; get current input file pointer
- MOV BX,INHANDLE
- XOR CX,CX
- XOR DX,DX
- INT 21h
- JNC >L5
- JMP MAKEHEADER_READERR
- L5:
- MOV DI,DX ; save in DI:SI
- MOV SI,AX
- MOV AX,4202h ; seek to end of input file
- MOV BX,INHANDLE
- XOR CX,CX
- XOR DX,DX
- INT 21h
- JNC >L6
- JMP MAKEHEADER_READERR
- L6:
- SUB AX,SI ; determine number of sample bytes
- SBB DX,DI
- MOV BX,AX ; divide high word by length of sample
- MOV AX,DX
- XOR DX,DX
- DIV INSAMPSIZE
- MOV WORD PTR INNSAMPLES+2,AX ; save high word of result
- MOV AX,BX ; divide low word + remainder
- DIV INSAMPSIZE
- MOV WORD PTR INNSAMPLES,AX ; save low word of result
- MOV AX,4200h ; seek back to start of sample data
- MOV BX,INHANDLE
- MOV CX,DI
- MOV DX,SI
- INT 21h
- JC MAKEHEADER_READERR
- ;
- ; Determine number of output samples.
- ;
- MOV AX,WORD PTR INNSAMPLES
- MOV DX,WORD PTR INNSAMPLES+2
- CMP HALVEFLAG,0
- JE >L7
- ADD AX,1 ; divide by 2 and round up
- ADC DX,0
- SHR DX,1
- RCR AX,1
- L7:
- MOV WORD PTR NSAMPLES,AX
- MOV WORD PTR NSAMPLES+2,DX
- ;
- ; Determine length of output data.
- ;
- MUL SAMPSIZE
- MOV WORD PTR DATALEN,AX
- MOV BX,DX
- MOV AX,WORD PTR NSAMPLES+2
- MUL SAMPSIZE
- ADD AX,BX
- MOV WORD PTR DATALEN+2,AX
- ;
- ; Determine length of output .wav file.
- ;
- MOV DX,AX
- MOV AX,WORD PTR DATALEN
- ADD AX,36
- ADC DX,0
- MOV WORD PTR WAVLEN,AX
- MOV WORD PTR WAVLEN+2,DX
- ;
- ; Write the header out.
- ;
- MOV AH,40h
- MOV BX,OUTHANDLE
- MOV CX,44
- MOV DX,WAVHEADER
- INT 21h
- JC MAKEHEADER_WRITEERR
- POP DI
- POP SI
- POP DX
- POP CX
- POP BX
- POP AX
- RET
- ;
- ; Seek error on input file.
- ;
- MAKEHEADER_READERR:
- MOV DX,OFFSET READERRMSG
- JMP ERROR
- ;
- ; Error writing to output file.
- ;
- MAKEHEADER_WRITEERR:
- MOV DX,OFFSET WRITEERRMSG
- JMP ERROR
- ;
- ; Subroutine, reads data into input buffer, sufficient to make 1024 output
- ; samples. The amount of data successfully read is returned in CX - the
- ; equivalent in output samples of the data successfully read. Halts the
- ; program with an error message on file error.
- ;
- READDATA:
- PUSH AX
- PUSH BX
- PUSH DX
- ;
- ; Read from the input file.
- ;
- PUSH DS
- MOV AH,3Fh
- MOV BX,INHANDLE
- MOV CX,BYTESTOREAD
- MOV DX,SEG INBUF
- MOV DS,DX
- XOR DX,DX
- INT 21h
- POP DS
- JC READDATA_READERR
- ;
- ; Check if the full amount requested was read.
- ;
- CMP AX,CX
- JB READDATA_EOF
- ;
- ; Got the full amount.
- ;
- MOV CX,1024
- JMP READDATA_EXIT
- ;
- ; End of file - less than the full amount requested was read.
- ;
- READDATA_EOF:
- XOR DX,DX ; calculate number of samples read
- DIV INSAMPSIZE
- MOV CX,AX
- CMP HALVEFLAG,0 ; if not halving, exit
- JE READDATA_EXIT
- INC CX ; halving - divide by 2 and round up
- SHR CX,1
- ;
- ; Exit.
- ;
- READDATA_EXIT:
- POP DX
- POP BX
- POP AX
- RET
- ;
- ; Error reading input file.
- ;
- READDATA_READERR:
- MOV DX,OFFSET READERRMSG
- JMP ERROR
- ;
- ; Subroutine to write out the output buffer contents. Halts the program
- ; with an error message if unsuccessful. Uses BYTESPLACED.
- ;
- WRITEDATA:
- PUSH AX
- PUSH BX
- PUSH CX
- PUSH DX
- PUSH DS
- MOV AH,40h
- MOV BX,OUTHANDLE
- MOV CX,BYTESPLACED
- MOV DX,SEG OUTBUF
- MOV DS,DX
- XOR DX,DX
- INT 21h
- POP DS
- JC WRITEDATA_WRITEERR
- POP DX
- POP CX
- POP BX
- POP AX
- RET
- ;
- ; Error writing to output file.
- ;
- WRITEDATA_WRITEERR:
- MOV DX,OFFSET WRITEERRMSG
- JMP ERROR
- ;
- ; Subroutine to copy a multichannel sample from the input buffer to the small
- ; buffer in the data segment.
- ;
- COPYSAMPLE:
- PUSH AX
- PUSH CX
- PUSH SI
- PUSH DI
- PUSH DS
- PUSH ES
- MOV CX,INSAMPSIZE ; CX = number of bytes to copy
- MOV SI,BYTESTAKEN ; DS:SI -> bytes to copy
- PUSH DS
- POP ES ; ES:DI -> small buffer
- MOV AX,SEG INBUF
- MOV DS,AX
- MOV DI,OFFSET SMALLBUF
- REP MOVSB
- POP ES
- POP DS
- MOV BYTESTAKEN,SI
- POP DI
- POP SI
- POP CX
- POP AX
- RET
- ;
- ; Subroutine to convert the multichannel sample in the small buffer to 8-bit.
- ;
- CONVTO8:
- CMP INCHANSIZE,1 ; if already 8-bit, return immediately
- JNE CONVTO8_PROCEED
- RET
- CONVTO8_PROCEED:
- PUSH AX
- PUSH CX
- PUSH SI
- PUSH DI
- PUSH ES
- MOV SI,OFFSET SMALLBUF
- PUSH DS
- POP ES
- MOV DI,SI
- MOV CX,INNCHANNELS
- CONVTO8_CHANLOOP:
- ADD SI,SKIPLENGTH
- LODSB
- ADD AL,128
- STOSB
- LOOP CONVTO8_CHANLOOP
- POP ES
- POP DI
- POP SI
- POP CX
- POP AX
- RET
- ;
- ; Subroutine to mix a multichannel 8-bit unsigned sample. The sample is
- ; assumed to be in the small buffer, and ES and DS are assumed to address
- ; the data segment. The result is placed back in the small buffer.
- ;
- MIX8:
- PUSH AX
- PUSH BX
- PUSH CX
- PUSH DX
- PUSH SI
- ;
- ; DS:SI addresses the sample.
- ;
- MOV SI,OFFSET SMALLBUF
- ;
- ; Add up the channels.
- ;
- MOV CX,INNCHANNELS
- XOR BX,BX
- MIX8_ADDLOOP:
- LODSB
- ADD BL,AL
- ADC BH,0
- LOOP MIX8_ADDLOOP
- ;
- ; Divide by the number of input channels and save result.
- ;
- MOV AX,BX
- XOR DX,DX
- DIV INNCHANNELS
- MOV SMALLBUF,AL
- POP SI
- POP DX
- POP CX
- POP BX
- POP AX
- RET
- ;
- ; Subroutine to mix a multichannel 16 (or more)-bit signed sample. The
- ; sample is assumed to be in the small buffer, and ES and DS are assumed
- ; to address the data segment. The result is placed back in the small
- ; buffer.
- ;
- MIX16:
- PUSH AX
- PUSH BX
- PUSH CX
- PUSH DX
- PUSH SI
- PUSH DI
- ;
- ; Clear the mixing buffer.
- ;
- MOV DI,OFFSET MIXBUF
- MOV AL,0
- MOV CX,CHANSIZE
- REP STOSB
- STOSB
- ;
- ; Loop over the channels.
- ;
- MOV SI,OFFSET SMALLBUF
- MOV CX,INNCHANNELS
- MIX16_ADDLOOP:
- PUSH CX
- ;
- ; Copy all but the last byte of the channel to the channel buffer.
- ;
- MOV CX,CHANSIZE ; CHANSIZE is at least 2 for signed
- DEC CX
- MOV DI,OFFSET CHANBUF
- REP MOVSB
- ;
- ; Get the last byte, sign extend, add to convert to unsigned, and
- ; place in the channel buffer.
- ;
- LODSB
- CBW
- ADD AX,80h
- MOV [DI],AX
- ;
- ; Add the channel to the mixing buffer.
- ;
- PUSH SI
- MOV SI,OFFSET CHANBUF
- MOV DI,OFFSET MIXBUF
- MOV CX,CHANSIZE
- INC CX
- CLC
- LAHF
- MIX16_DOADD:
- LODSB
- SAHF
- ADC [DI],AL
- LAHF
- INC DI
- LOOP MIX16_DOADD
- POP SI
- ;
- ; Go to next channel.
- ;
- POP CX
- LOOP MIX16_ADDLOOP
- ;
- ; Divide by the number of channels.
- ;
- STD ; set direction to decrement
- MOV CX,CHANSIZE
- MOV SI,CX
- ADD SI,OFFSET MIXBUF
- DEC CX
- MOV DI,CX
- ADD DI,OFFSET SMALLBUF
- ;
- ; Divide high 2 bytes as word to avoid overflow.
- ;
- LODSB
- MOV AH,AL
- LODSB
- XOR DX,DX
- DIV INNCHANNELS
- STOSB ; discard high 8 bits of quotient
- MOV AH,DL
- ;
- ; Complete the division.
- ;
- MOV BL,BYTE PTR INNCHANNELS
- MIX16_DIVLOOP:
- LODSB
- DIV BL
- STOSB
- LOOP MIX16_DIVLOOP
- CLD ; restore direction flag
- ;
- ; Convert the result to signed.
- ;
- MOV SI,OFFSET SMALLBUF-1
- ADD SI,CHANSIZE
- ADD BYTE PTR [SI],80h
- POP DI
- POP SI
- POP DX
- POP CX
- POP BX
- POP AX
- RET
- ;
- ; Subroutine to mix the multichannel sample in the small buffer to mono.
- ; Assumes that the sample has already been converted to 8-bit if that was
- ; to be done. This version special-cases 8-bit samples.
- ;
- ; Exit immediately if already mono.
- ;
- DOMIX:
- CMP INNCHANNELS,1
- JNE DOMIX_PROCEED
- RET
- ;
- ; Both DS and ES address the data segment.
- ;
- DOMIX_PROCEED:
- PUSH ES
- PUSH DS
- POP ES
- ;
- ; Call the appropriate subroutine for signed or unsigned (8-bit)
- ; samples.
- ;
- CMP ISSIGNED,0
- JE DOMIX_8BIT
- CALL MIX16
- JMP DOMIX_EXIT
- DOMIX_8BIT:
- CALL MIX8
- DOMIX_EXIT:
- POP ES
- RET
- ;
- ; Subroutine to copy a sample from the small buffer to the output buffer.
- ; Uses and updates BYTESPLACED.
- ;
- PUTSAMPLE:
- PUSH CX
- PUSH SI
- PUSH DI
- PUSH ES
- MOV CX,SEG OUTBUF
- MOV ES,CX
- MOV DI,BYTESPLACED
- MOV SI,OFFSET SMALLBUF
- MOV CX,SAMPSIZE
- REP MOVSB
- MOV BYTESPLACED,DI
- POP ES
- POP DI
- POP SI
- POP CX
- RET
- ;
- ; Routine to perform the actual conversion of samples in the input file and
- ; write the results to the output file. Samples are converted 1024 output
- ; samples at a time; if HALVEFLAG is on, this will be 2048 input samples.
- ;
- PERFORM:
- PUSH AX
- ;
- ; Determine number of input samples for each pass.
- ;
- CMP HALVEFLAG,0
- JE PERFORM_1024
- MOV SAMPTOREAD,2048
- PERFORM_1024:
- ;
- ; Determine number of input bytes for each pass.
- ;
- MOV AX,SAMPTOREAD
- MUL INSAMPSIZE
- MOV BYTESTOREAD,AX
- ;
- ; Determine number of bytes to "skip over" from each input channel.
- ;
- MOV AX,INCHANSIZE
- SUB AX,CHANSIZE
- MOV SKIPLENGTH,AX
- ;
- ; Main loop. Read data (BYTESTOREAD bytes) from the input file into
- ; the input buffer.
- ;
- PERFORM_MAINLOOP:
- CALL READDATA
- JCXZ PERFORM_MAINLPEND ; exit loop and end of file
- ;
- ; Number of bytes taken from the input buffer set to zero.
- ;
- MOV BYTESTAKEN,0
- ;
- ; Number of bytes placed in output buffer set to zero.
- ;
- MOV BYTESPLACED,0
- ;
- ; Loop over samples in the buffer.
- ;
- PERFORM_SAMPLOOP:
- CALL COPYSAMPLE ; get a sample in the small buffer
- ;
- ; If converting to 8-bit, do the conversion.
- ;
- CMP CONVERTFLAG,0
- JE PERFORM_CHKMIX
- CALL CONVTO8
- ;
- ; If mixing to mono, do so.
- ;
- PERFORM_CHKMIX:
- CMP MIXFLAG,0
- JE PERFORM_DOPUT
- CALL DOMIX
- ;
- ; Put the sample in the output buffer.
- ;
- PERFORM_DOPUT:
- CALL PUTSAMPLE
- ;
- ; If halving the sampling rate, skip a sample.
- ;
- CMP HALVEFLAG,0
- JE PERFORM_SAMPLPTEST
- MOV AX,BYTESTAKEN
- ADD AX,INSAMPSIZE
- MOV BYTESTAKEN,AX
- PERFORM_SAMPLPTEST:
- LOOP PERFORM_SAMPLOOP
- ;
- ; Write the output buffer and go read more data.
- ;
- CALL WRITEDATA
- JMP PERFORM_MAINLOOP
- PERFORM_MAINLPEND:
- POP AX
- RET
- ;
- ; Main program.
- ;
- MAIN:
- ;
- ; DS addresses data segment (always - if any routine changes DS, it
- ; must change it back before returning).
- ;
- MOV AX,SEG SDATA
- MOV DS,AX
- ;
- ; Set direction to increment (always - if any routine changes DF, it
- ; must restore it before returning).
- ;
- CLD
- ;
- ; Open input file and create output file.
- ;
- CALL OPENFILES
- ;
- ; Read the .wav header from the input file.
- ;
- CALL GETWAVHEADER
- ;
- ; Display the header information for the user.
- ;
- CALL SHOWWAVINFO
- ;
- ; Get desired actions from user.
- ;
- CALL GETACTIONS
- ;
- ; Fill in fields in the output .wav header and write it out.
- ;
- CALL MAKEHEADER
- ;
- ; Convert the file.
- ;
- CALL PERFORM
- ;
- ; Terminate.
- ;
- MOV AX,4C00h
- INT 21h
- SCODE ENDS
- END MAIN